import 'dart:async';
import 'dart:collection';
import 'dart:ui';

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void runAutoApp(Widget app) {
  GwAutoWidgetsFlutterBinding.ensureInitialized()
    // ignore: invalid_use_of_protected_member
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

class GwAutoWidgetsFlutterBinding extends WidgetsFlutterBinding {
  static WidgetsBinding? _instance;
  static WidgetsBinding ensureInitialized() {
    if (GwAutoWidgetsFlutterBinding._instance == null) {
      GwAutoWidgetsFlutterBinding();
    }
    return WidgetsBinding.instance;
  }

  @override
  ViewConfiguration createViewConfiguration() {
    return ViewConfiguration(
      size: GwAutoSizeUtil.getSize(),
      devicePixelRatio: GwAutoSizeUtil.getDevicePixelRatio(),
    );
  }

  /// 因为修改了 devicePixelRatio ，得适配点击事件  GestureBinding
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;
  }

  @override
  void unlocked() {
    super.unlocked();
    _flushPointerEventQueue();
  }

  final Queue<PointerEvent> _pendingPointerEvents = Queue<PointerEvent>();

  void _handlePointerDataPacket(PointerDataPacket packet) {
    _pendingPointerEvents.addAll(PointerEventConverter.expand(
        packet.data,
        // 适配事件的转换比率,采用我们修改的
        GwAutoSizeUtil.getDevicePixelRatio()));
    if (!locked) _flushPointerEventQueue();
  }

  @override
  void cancelPointer(int pointer) {
    if (_pendingPointerEvents.isEmpty && !locked)
      scheduleMicrotask(_flushPointerEventQueue);
    _pendingPointerEvents.addFirst(PointerCancelEvent(pointer: pointer));
  }

  void _flushPointerEventQueue() {
    assert(!locked);
    while (_pendingPointerEvents.isNotEmpty)
      _handlePointerEvent(_pendingPointerEvents.removeFirst());
  }

  final Map<int, HitTestResult> _hitTests = <int, HitTestResult>{};

  void _handlePointerEvent(PointerEvent event) {
    assert(!locked);
    HitTestResult result;
    if (event is PointerDownEvent) {
      assert(!_hitTests.containsKey(event.pointer));
      result = HitTestResult();
      hitTest(result, event.position);
      _hitTests[event.pointer] = result;
      assert(() {
        if (debugPrintHitTestResults) debugPrint('$event: $result');
        return true;
      }());
    } else if (event is PointerUpEvent || event is PointerCancelEvent) {
      result = _hitTests.remove(event.pointer)!;
    } else if (event.down) {
      result = _hitTests[event.pointer]!;
    } else {
      return; // We currently ignore add, remove, and hover move events.
    }
    if (result != null) dispatchEvent(event, result);
  }
}

class GwAutoSizeUtil {
  static double _devicePixelRatio = 3.0;

  static double _screenWidth = 750;

  static double _screenHeight = 1334;

  static double _screenStandard = 750;

  static Size _screenSize = Size.zero;

  static bool _autoTextSize = true;

  static double _windowDevicePixelRatio = 1.0;

  /// 如果是横屏 就以高度为基准
  /// 如果是竖屏 就以宽度为基准
  /// 是否随着系统的文字大小而改变，默认是改变
  static void setStandard(double standard, {bool isAutoTextSize = true}) {
    _screenStandard = standard;
    _autoTextSize = isAutoTextSize;
  }

  /// 根据设置 的 宽度 来得到 devicePixelRatio
  static double getDevicePixelRatio() {
    _windowDevicePixelRatio = window.devicePixelRatio;
    var originalSize = window.physicalSize / _windowDevicePixelRatio;
    var originalWidth = originalSize.width;
    var originalHeight = originalSize.height;
    if (originalHeight > originalWidth) {
      // 竖屏
      _devicePixelRatio =
          window.physicalSize.width / GwAutoSizeUtil._screenStandard;
    } else {
      // 横屏
      _devicePixelRatio =
          window.physicalSize.height / GwAutoSizeUtil._screenStandard;
    }
    // Text("")
    return _windowDevicePixelRatio;
  }

  /// 根据设置的宽度，来得到对应的高度
  static Size getSize() {
    // 如果是横屏就已宽度为基准

    var originalSize = window.physicalSize / window.devicePixelRatio;
    var originalWidth = originalSize.width;
    var originalHeight = originalSize.height;
    if (originalHeight > originalWidth) {
      // 竖屏
      _screenHeight = window.physicalSize.height / getDevicePixelRatio();
      _screenWidth = _screenStandard;
      _screenSize = Size(_screenStandard, _screenHeight);
      return _screenSize;
    } else {
      // 横屏
      _screenWidth = window.physicalSize.width / getDevicePixelRatio();
      _screenHeight = _screenStandard;
      _screenSize = Size(_screenWidth, _screenStandard);
      return _screenSize;
    }
  }

  static Size getScreenSize() {
    return _screenSize;
  }

  static Widget appBuilder(BuildContext context, Widget? widget) {
    EdgeInsets viewInsets = MediaQuery.of(context).viewInsets;
    EdgeInsets pading = MediaQuery.of(context).padding;
    EdgeInsets viewPadding = MediaQuery.of(context).viewPadding;
    var adapterEdge = EdgeInsets.fromLTRB(viewInsets.left, viewInsets.top,
        viewInsets.right, getRealSize(viewInsets.bottom));
    var adapterPadding = EdgeInsets.fromLTRB(pading.left,
        getRealSize(pading.top), pading.right, getRealSize(pading.bottom));
    var adapterViewPadding = EdgeInsets.fromLTRB(
        viewPadding.left,
        getRealSize(viewPadding.top),
        viewPadding.right,
        getRealSize(viewPadding.bottom));

    return MediaQuery(
      // 这里如果设置 textScaleFactor = 1.0 ，就不会随着系统字体大小去改变了
      data: MediaQuery.of(context).copyWith(
          size: Size(GwAutoSizeUtil._screenWidth, GwAutoSizeUtil._screenHeight),
          devicePixelRatio: GwAutoSizeUtil._devicePixelRatio,
          textScaleFactor:
              _autoTextSize ? MediaQuery.of(context).textScaleFactor : 1.0,
          viewInsets: adapterEdge,
          padding: adapterPadding,
          viewPadding: adapterViewPadding),
      child: _adapterTheme(context, widget),
    );
  }

  /// 获取真正的大小，比如 kToolbarHeight kBottomNavigationBarHeight
  static double getRealSize(double size) {
    return size / (_devicePixelRatio / window.devicePixelRatio);
  }

  static _adapterTheme(BuildContext context, Widget? widget) {
    return Theme(
      data: Theme.of(context).copyWith(),
      child: widget!,
    );
  }
}
